package com.freecloud.spring.formework.context;

import com.freecloud.spring.formework.annotation.FCAutowired;
import com.freecloud.spring.formework.annotation.FCController;
import com.freecloud.spring.formework.annotation.FCService;
import com.freecloud.spring.formework.beans.FCBeanFacotry;
import com.freecloud.spring.formework.beans.FCBeanWrapper;
import com.freecloud.spring.formework.beans.config.FCBeanDefinition;
import com.freecloud.spring.formework.beans.config.FCBeanPostProcessor;
import com.freecloud.spring.formework.beans.support.FCBeanDefinitionReader;
import com.freecloud.spring.formework.beans.support.FCDefaultListableBeanFactory;

import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Author: maomao
 * @Date: 2019-12-10 09:21
 */
public class FCApplicationContext extends FCDefaultListableBeanFactory implements FCBeanFacotry {

    private String[] configLocations;

    private FCBeanDefinitionReader reader;
    /**
     * 单例对象缓存
     */
    private Map<String,Object> singletonObjects = new ConcurrentHashMap<>(256);
    /**
     * 通用的IOC容器
     */
    private Map<String,FCBeanWrapper> factoryBeanInstanceCache = new ConcurrentHashMap<>();

    public FCApplicationContext(String... configLocations){
        this.configLocations = configLocations;

        try {
            refresh();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public Object getBean(String beanName) throws Exception {

        FCBeanDefinition beanDefinition = this.beanDefinitionMap.get(beanName);

        // 1. 初始化
        Object instance = instantiateBean(beanName,beanDefinition);

        //工厂模式 + 策略模式，调用实现（在类加载前后处理占位）
        FCBeanPostProcessor postProcessor = new FCBeanPostProcessor();

        postProcessor.postProcessBeforeInitialization(instance,beanName);

        //因循环依赖问题，所以不在初始化中注入
        //class A{ B b;}
        //class B{ A a;}

        // 2. 把BeanWrapper保存到IOC容器中
        FCBeanWrapper beanWrapper = getBeanWrapper( instance ,beanName);

        //注入前通知
        postProcessor.postProcessAfterInitialization(instance,beanName);
        // 3. 注入
        populateBean(beanName,beanDefinition,beanWrapper);


        return this.factoryBeanInstanceCache.get(beanName).getWrappedInstance();
    }

    private FCBeanWrapper getBeanWrapper(Object instance ,String beanName){

        // 把这个对象封装到BeanWrapper中
        FCBeanWrapper  beanWrapper = new FCBeanWrapper(instance);
        // 把BeanWrapper存在IOC容器中
        //singletonObjects(保存单例对象)
        //factoryBeanInstanceCache(真正的IOC容器 Map<String,BeanWrapper>)

        //TODO 因扫描时加载的类是无顺序的，导致某些类的依赖是接口时，还没注入，导致获取时异常
        Class<?>[] interfaces = instance.getClass().getInterfaces();
        if(interfaces.length > 0){
            for(int i = 0;i < interfaces.length;i++){
                this.factoryBeanInstanceCache.put(interfaces[i].getName(),beanWrapper);
            }
        }
        this.factoryBeanInstanceCache.put(beanName,beanWrapper);

        return beanWrapper;
    }

    @Override
    public Object getBean(Class<?> clazz) throws Exception {
        return getBean(clazz.getName());
    }

    public String[] getBeanDefinitionNames(){
        return this.beanDefinitionMap.keySet().toArray(new String[this.beanDefinitionMap.size()]);
    }

    public int getBeanDefinitionCount(){
        return this.beanDefinitionMap.size();
    }

    private void populateBean(String beanName, FCBeanDefinition fcBeanDefinition, FCBeanWrapper fcBeanWrapper) {

        Object instance = fcBeanWrapper.getWrappedInstance();


        Class<?> clazz = fcBeanWrapper.getWrappedCalss();
        //判断只有加了注解的类，才执行依赖注入
        if (!clazz.isAnnotationPresent(FCController.class) && !clazz.isAnnotationPresent(FCService.class)){
            return;
        }

        Field[] fields = clazz.getDeclaredFields();
        Object autoWiredInstance;

        for (Field field : fields) {
            if(!field.isAnnotationPresent(FCAutowired.class)){
                continue;
            }

            FCAutowired autowired = field.getAnnotation(FCAutowired.class);
            String autowiredBeanName = autowired.value().trim();
            if("".equals(autowiredBeanName)){
                autowiredBeanName = field.getType().getName();
            }
            //强制访问
            field.setAccessible(true);

            try {
                //todo 为什么会为NULL，先留个坑
                // 因扫描时加载的类是无顺序的，导致某些类的依赖是接口时，还没注入，导致获取时异常
//                if(this.factoryBeanInstanceCache.get(autowiredBeanName) == null){
//                    continue;
//                }
                autoWiredInstance = this.factoryBeanInstanceCache.get(autowiredBeanName).getWrappedInstance();
                if(autoWiredInstance == null){
                    throw new Exception("The " + autowiredBeanName + " is null !!!");
                }
                field.set(instance,autoWiredInstance);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }


    }

    private Object instantiateBean(String beanName, FCBeanDefinition fcBeanDefinition) {
        //1. 拿到实例化的类名

        String className = fcBeanDefinition.getBeanClassName();

        //2. 反射实例化，得到一个对象
        Object instance = null;
        try {
            if(this.singletonObjects.containsKey(className)){
                instance = this.singletonObjects.get(className);
            }else {
                Class<?> clazz =  Class.forName(className);
                instance = clazz.newInstance();
                this.singletonObjects.put(className,instance);
                this.singletonObjects.put(fcBeanDefinition.getFactoryBeanName(),instance);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return instance;
    }

    @Override
    protected void refresh() throws Exception {
        //1、定位配置
        reader = new FCBeanDefinitionReader(this.configLocations);
        
        //2、加载配置文件，扫描相关的类，把他们封装成BeanDefinition
        List<FCBeanDefinition> beanDefinitionList = reader.loadBeanDefinitions();

        //3、注册，把配置信息放到容器中（伪IOC容器）
        doRegisterBeanDefinition(beanDefinitionList);

        //4、不是延时加载的类要提前初始化
        doAutowrited();
    }

    /**
     * 只处理非延时加载的情况
     */
    private void doAutowrited() throws Exception {

        for (Map.Entry<String,FCBeanDefinition> beanDefinitionEntry: super.beanDefinitionMap.entrySet() ) {
            String beanName = beanDefinitionEntry.getKey();
            if(!beanDefinitionEntry.getValue().isLazyInit()){
                getBean(beanName);
            }
        }
    }

    private void doRegisterBeanDefinition(List<FCBeanDefinition> beanDefinitionList) {
        if(beanDefinitionList == null){
            return;
        }
        //如果有接口，或者是以注解形式
        for (FCBeanDefinition beanDefinition: beanDefinitionList) {
            super.beanDefinitionMap.put(beanDefinition.getFactoryBeanName(),beanDefinition);
        }
    }

    public Properties getConfig(){
        return this.reader.getConfig();
    }
}
